using System;

namespace XmlObjects
{

  /// <summary>
  /// Attribute set at class level to define how the type will behave in relationship
  ///   to the reading of xml data.
  /// </summary>
  [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
  public class XmlTypeAttribute : System.Attribute
  {
    /// <summary>
    /// InnerTextField identifies which field in the class will hold the innerText,
    ///   if any, for the given type. If not defined, and text is provided, it will
    ///   be used the static Parse(String) method -if the class defines no attributes or
    ///   elements-, and, if not found, an error will  be raised
    /// Note that the type of this field does not need to be string
    /// </summary>
    public string InnerTextField
    {
      get { return innerTextField; }
      set 
      { 
        innerTextField = value;
        innerTextDefined = true;
      }
    }

    /// <summary>
    /// ParentField identifies which field in the class will hold the parent node.
    ///   If not provided, this information will not be stored.
    /// </summary>
    public string ParentField
    {
      get { return parentField; }
      set 
      { 
        parentField = value;
        parentDefined = true;
      }
    }

    /// <summary>
    /// ChildsField identifies which field in the class will hold the collection,
    ///   of childs. If not provided, this information will not be stored.
    /// Note that the type of this field must be an array (suitable to hold
    ///   the childs)
    /// </summary>
    public string ChildsField
    {
      get { return childsField; }
      set 
      { 
        childsField = value;
        childsDefined = true;
      }
    }


    /// <summary>
    /// GenericElementField identifies which field in the class will hold 
    ///   elements not handled by the normal definitions.
    /// The type must be array - the name of the elements is not stored
    /// </summary>
    public string GenericElementField
    {
      get { return genericElementField; }
      set
      {
        genericElementField = value;
        genericElementDefined = true;
      }
    }


    /// <summary>
    /// XmlReadCompletedMethod identifies the method to be called 
    ///   when the type is fully built
    /// </summary>
    public string XmlReadCompletedMethod
    {
      get { return readCompletedMethod; }
      set
      {
        readCompletedMethod = value;
        readCompletedDefined = true;
      }
    }



    /// <summary>
    /// XmlWriteCompletedMethod identifies the method to be called 
    ///   when the type is completely saved
    /// </summary>
    public string XmlWriteCompletedMethod
    {
      get { return writeCompletedMethod; }
      set
      {
        writeCompletedMethod = value;
        writeCompletedDefined = true;
      }
    }



    /// <summary>
    /// XmlReadCompletedMethod identifies the method to be called 
    ///   when the type is fully built
    /// </summary>
    public string XmlWritePrepareMethod
    {
      get { return startedMethod; }
      set
      {
        startedMethod = value;
        startedMethodDefined = true;
      }
    }



    /// <summary>
    /// TypeAttribute identifies the attribute used in the Xml definition
    ///   to hold, if needed, the type of the associated class. The name
    ///   is automatically converted to lowercase
    /// </summary>
    public string TypeAttribute
    {
      get { return typeAttribute; }
      set
      {
        typeAttribute = value==null? value : value.ToLower();
        typeAttributeDefined = true;
      }
    }



    /// <summary>
    /// Setting the ExplicitFields property means that the class must explictly 
    ///   define the fields with Xml mapping. If unset, any public field has
    ///   Xml mapping
    /// </summary>
    public bool ExplicitFields
    {
      get { return explicitFields; }
      set 
      { 
        explicitFields = value;
        explicitFieldsDefined = true;
      }
    }


    /// <summary>
    /// This property affect on how to handle primitives, enumerations and strings.
    /// By default, these types are mapped to Xml attributes, unless this property 
    ///    is set to False
    /// </summary>
    public bool AttributeFields
    {
      get { return attributeFields; }
      set 
      { 
        attributeFields = value;
        attributeFieldsDefined = true;
      }
    }


    /// <summary>
    /// This property affects on when to use ProvidedFieldsSupport or not.
    /// If so, fields with special termination (AttributeProvided or ElementProvided),
    ///   found after boolean fields already defined, are used to know when the
    ///   associated attribute or (simple) element was defined
    /// </summary>
    public bool ProvidedFieldsSupport
    {
      get { return providedFieldsSupport; }
      set 
      { 
        providedFieldsSupport = value;
        providedFieldsSupportDefined = true;
      }
    }


    /// <summary>
    /// Returns true if the inner text field was explicitly defined
    /// </summary>
    public bool InnerTextFieldDefined
    {
      get { return innerTextDefined; }
    }



    /// <summary>
    /// Returns true if the parent field was explicitly defined
    /// </summary>
    public bool ParentFieldDefined
    {
      get { return parentDefined; }
    }



    /// <summary>
    /// Returns true if the childs field was explicitly defined
    /// </summary>
    public bool ChildsFieldDefined
    {
      get { return childsDefined; }
    }


    /// <summary>
    /// Returns true if the read completed method was defined
    /// </summary>
    public bool ReadCompletedMethodDefined
    {
      get { return readCompletedDefined; }
    }


    /// <summary>
    /// Returns true if the write completed method was defined
    /// </summary>
    public bool WriteCompletedMethodDefined
    {
      get { return writeCompletedDefined; }
    }


    /// <summary>
    /// Returns true if the started method was defined
    /// </summary>
    public bool WriteStartedMethodDefined
    {
      get { return startedMethodDefined; }
    }

    /// <summary>
    /// Returns true if the type attribute was defined
    /// </summary>
    public bool TypeAttributeDefined
    {
      get { return typeAttributeDefined; }
    }

    /// <summary>
    /// Returns true if the generic element was defined
    /// </summary>
    public bool GenericElementDefined
    {
      get { return genericElementDefined; }
    }

    /// <summary>
    /// Returns true if the explicit field element is defined
    /// </summary>
    public bool ExplicitFieldsDefined
    {
      get { return explicitFieldsDefined; }
    }

    /// <summary>
    /// Returns true if the attribute fields support element was defined
    /// </summary>
    public bool AttributeFieldsDefined
    {
      get { return attributeFieldsDefined; }
    }

    /// <summary>
    /// Returns true if the provided fields support element was defined
    /// </summary>
    public bool ProvidedFieldsSupportDefined
    {
      get { return providedFieldsSupportDefined; }
    }

    bool explicitFields = false;
    bool attributeFields = true;
    bool providedFieldsSupport = true;
    string innerTextField;
    string childsField;
    string parentField;
    string readCompletedMethod;
    string writeCompletedMethod;
    string typeAttribute;
    string startedMethod;
    string genericElementField;
    bool innerTextDefined, childsDefined, parentDefined, readCompletedDefined, typeAttributeDefined;
    bool startedMethodDefined, writeCompletedDefined, genericElementDefined;
    bool providedFieldsSupportDefined, explicitFieldsDefined, attributeFieldsDefined;
  }

  
  
  
  
  /// <summary>
  /// Attribute that can be set on object's fields to set the associated xml name
  /// If the field is not an array, it is possible to set AsElement=True
  /// </summary>
  [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
  public class XmlFieldAttribute : System.Attribute
  {
    /// <summary>
    /// Defines whether the field is an attribute, element, or has not been defined
    /// </summary>
    public enum XmlFieldType { Attribute, Element, NotDefined };

    public XmlFieldAttribute()
    {
      Name = null;
    }

    public XmlFieldAttribute(String name)
    {
      Name = name;
    }

    public string Name
    {
      get { return this.name; }
      set { this.name = value; }
    }

    /// <summary>
    /// Simple types (primitives, enumerations and strings) are handled 
    ///   normally as attributes. 
    /// Setting AsElement=True in a non-array type maps it instead to an element
    /// </summary>
    public bool AsElement
    {
      set { fieldType = value ? XmlFieldType.Element : XmlFieldType.Attribute; }
      get { return fieldType == XmlFieldType.Element; }
    }

    /// <summary>
    /// XmlFieldType represents the boolean AsElement plus a 'not-defined' state
    /// </summary>
    /// <returns></returns>
    public XmlFieldType GetFieldType()
    {
      return fieldType;
    }

    private string name;
    private XmlFieldType fieldType = XmlFieldType.NotDefined;
  }




  /// <summary>
  /// Attribute to explicitly define an object's field as not XmlField
  /// It is only useful if the class does not define an XmlTypeATtribyte with
  ///   ExplicitFields=true
  /// </summary>
  [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
  public class XmlNoFieldAttribute : System.Attribute { }


}
